{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "DeepFace analyze method wraps age, gender and race prediction models. Those models build VGG-Face first, modify its output layer and load pre-trained weights for those sub models. Each model size is 500 MB.\n",
    "\n",
    "However, we can build VGG-Face once and find its early layer output once. Then, transfer this early layer output to 3 layer basic network. In this way, the size of the all age, gender and race model would decrease from 500 MB to 2 MB. \n",
    "\n",
    "This notebook makes those sub models into smaller ones."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import keras\n",
    "from keras.preprocessing import image\n",
    "from keras.callbacks import ModelCheckpoint,EarlyStopping\n",
    "from keras.layers import Dense, Activation, Dropout, Flatten, Input, Convolution2D, ZeroPadding2D, MaxPooling2D, Activation\n",
    "from keras.layers import Conv2D, AveragePooling2D\n",
    "from keras.models import Model, Sequential\n",
    "import keras.backend as K"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from deepface import DeepFace\n",
    "from deepface.commons import functions\n",
    "from deepface.extendedmodels import Age"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VGG-Face"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#the both if and else blocks work well\n",
    "if True:\n",
    "    model = DeepFace.build_model('VGG-Face')\n",
    "else:\n",
    "    model = Sequential()\n",
    "    model.add(ZeroPadding2D((1,1),input_shape=(224,224, 3)))\n",
    "    model.add(Convolution2D(64, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(64, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D((2,2), strides=(2,2)))\n",
    "\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(128, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(128, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D((2,2), strides=(2,2)))\n",
    "\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(256, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(256, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(256, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D((2,2), strides=(2,2)))\n",
    "\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D((2,2), strides=(2,2)))\n",
    "\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(ZeroPadding2D((1,1)))\n",
    "    model.add(Convolution2D(512, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D((2,2), strides=(2,2)))\n",
    "\n",
    "    model.add(Convolution2D(4096, (7, 7), activation='relu'))\n",
    "    model.add(Dropout(0.5))\n",
    "    model.add(Convolution2D(4096, (1, 1), activation='relu'))\n",
    "    model.add(Dropout(0.5))\n",
    "    model.add(Convolution2D(2622, (1, 1)))\n",
    "    model.add(Flatten())\n",
    "    model.add(Activation('softmax'))\n",
    "\n",
    "    model.load_weights('C:/Users/IS96273/.deepface/weights/vgg_face_weights.h5')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## V1 model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "if True:\n",
    "    classes = 101\n",
    "\n",
    "    base_model_output = Sequential()\n",
    "    base_model_output = Convolution2D(classes, (1, 1), name='predictions')(model.layers[-4].output)\n",
    "    base_model_output = Flatten()(base_model_output)\n",
    "    base_model_output = Activation('softmax')(base_model_output)\n",
    "\n",
    "    age_model = Model(inputs=model.input, outputs=base_model_output)\n",
    "\n",
    "    age_model.load_weights(\"C:/Users/IS96273/.deepface/weights/age_model_weights.h5\")\n",
    "else:\n",
    "    #else block causes trouble. I cannot understand why.\n",
    "    age_model = DeepFace.build_model('Age')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## V2 model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "common_model = Model(inputs = model.input, outputs = model.layers[-4].output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "age_model_v2 = Sequential()\n",
    "age_model_v2.add(Convolution2D(101, (1, 1), input_shape=(1, 1, 4096)))\n",
    "age_model_v2.add(Flatten())\n",
    "age_model_v2.add(Activation('softmax'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "if True:\n",
    "    for i in range(0, 3):\n",
    "        age_model_v2.layers[i].set_weights(age_model.layers[-3+i].get_weights())\n",
    "    age_model_v2.save_weights(\"age_model_v2_weights.h5\")\n",
    "else:\n",
    "    age_model_v2.load_weights(\"age_model_v2_weights.h5\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "img_path = \"deepface/tests/dataset/img1.jpg\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "img = functions.preprocess_face(img_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 224, 224, 3)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "v1 result:  31.803729148267408\n"
     ]
    }
   ],
   "source": [
    "probas = age_model.predict(img)[0]\n",
    "print(\"v1 result: \", Age.findApparentAge(probas))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "v2 result:  31.803729148267408\n"
     ]
    }
   ],
   "source": [
    "common_output = common_model.predict(img)\n",
    "probas_v2 = age_model_v2.predict(common_output)\n",
    "print(\"v2 result: \", Age.findApparentAge(probas_v2))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
